/*
Pyxis 2.0
Copyright 2010-2011 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Collections;
using System.IO;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

using Skewworks.Pyxis.EXT;
using Skewworks.Pyxis.Kernel;

namespace Skewworks.Pyxis.GUI.Controls
{

    [Serializable]
    public class Filebox : Control
    {

        #region Enumerations

        private enum FileType
        {
            Folder = 0,
            File = 1,
            Application = 2,
            Bitmap = 3,
            GIF = 4,
            JPEG = 5,
        }

        #endregion

        #region Structures

        [Serializable]
        private struct FileDetails
        {
            public string Name;
            public string Size;
            public string Path;
            public Bitmap Icon;
            public bool IsFile;
        }

        #endregion

        #region Variables

        private string _path = "";
        private ArrayList _items;

        private int _selIndex = -1;
        private int _scrollY = 0;
        private int _mY = 0;
        private bool _bMoved = false;

        private Hashtable _filter = null;

        #endregion

        #region Events

        public event OnSelectedFileChanged SelectedFileChanged;

        /// <summary>
        /// Event for Taps
        /// </summary>
        /// <param name="sender"></param>
        protected virtual void OnSelectedFileChanged(Object sender, string path)
        {
            if (SelectedFileChanged != null) SelectedFileChanged(sender, path);
        }

        #endregion

        #region Constructors

        public Filebox(string path, int x, int y, int width, int height)
        {
            _x = x;
            _y = y;
            _w = width;
            _h = height;
            _path = path;
            UpdateFiles();
        }

        public Filebox(string path, string[] extensions, int x, int y, int width, int height)
        {
            _x = x;
            _y = y;
            _w = width;
            _h = height;
            _path = path;

            if (extensions != null)
            {
                _filter = new Hashtable();
                for (int i = 0; i < extensions.Length; i++)
                    _filter.Add(extensions[i], extensions[i]);
            }

            UpdateFiles();
        }

        #endregion

        #region Touch Invokes

        public override void TouchDown(object sender, point e)
        {
            if (!_enabled || !_visible) return;
            
            _mY = e.Y;
            _mDown = true;

            // Begin TapHold
            _ptTapHold = e;
            _eTapHold = TapState.THWaiting;
            Thread thHold = new Thread(TapHoldWaiter);
            thHold.Start();
        }

        public override void TouchUp(object sender, point e)
        {
            if (!_enabled || !_visible) return;

            // Check Tap Hold
            if (_eTapHold == TapState.THComplete)
            {
                _eTapHold = TapState.Normal;
                return;
            }
            _eTapHold = TapState.Normal;

            if (_mDown)
            {
                if (_bMoved)
                {
                    _bMoved = false;
                }
                else
                {
                    if (this.ScreenBounds.contains(e))
                    {
                        int y = e.Y - Top + -(_scrollY);
                        int index = 0;

                        y -= 21; // Column Headers

                        while (y > 20)
                        {
                            y -= 20;
                            index++;
                        }

                        if (index < _items.Count && index > -1)
                        {
                            _selIndex = index;
                            FileDetails fd = (FileDetails)_items[_selIndex];
                            OnSelectedFileChanged(this, fd.Path);
                            Render(true);

                        }

                        OnTap(this, new point(e.X - Left, e.Y - Top));
                    }
                }
                _mDown = false;
            }
        }

        public override void TouchMove(object sender, point e)
        {
            if (!_enabled || !_visible) return;
            _eTapHold = TapState.Normal;

            int sH = (_items.Count * 20) + 21;
            if (sH < _h - 2) return;
            int nScroll = _scrollY + (e.Y - _mY);
            if (nScroll > 0) nScroll = 0;
            if (nScroll < -sH + _h - 2) nScroll = -sH + _h - 2;
            _mY = e.Y;
            _bMoved = true;
            if (_scrollY != nScroll)
            {
                _scrollY = nScroll;
                Render(true);
            }
        }

        private void TapHoldWaiter()
        {
            Thread.Sleep(750);
            if (_eTapHold == TapState.Normal)
                return;

            // Update Selection
            if (this.ScreenBounds.contains(_ptTapHold))
            {
                int y = _ptTapHold.Y - Top + -(_scrollY);
                int index = 0;

                y -= 21; // Column Headers

                while (y > 20)
                {
                    y -= 20;
                    index++;
                }

                if (index < _items.Count && index > -1)
                {
                    _selIndex = index;
                    FileDetails fd = (FileDetails)_items[_selIndex];
                    OnSelectedFileChanged(this, fd.Path);
                    Render(true);

                }
            }

            // Fire event
            OnTapHold(this, _ptTapHold);
        }

        #endregion

        #region  Properties

        public string SelectedFile
        {
            get
            {
                if (_selIndex < 0) return String.Empty;
                FileDetails fd = (FileDetails)_items[_selIndex];
                return fd.Path;
            }
            set
            {
                FileDetails fd;
                for (int i = 0; i < _items.Count; i++)
                {
                    fd = (FileDetails)_items[i];
                    if (fd.Path == value)
                    {
                        _selIndex = i;
                        Render(true);
                        OnSelectedFileChanged(this, value);
                        break;
                    }
                }
            }
        }

        public bool SelectionIsFile
        {
            get
            {
                if (_selIndex < 0) return false;
                FileDetails fd = (FileDetails)_items[_selIndex];
                return fd.IsFile;
            }
        }

        public string Path
        {
            get { return _path; }
            set
            {
                if (_path == value) return;
                _path = value;
                Refresh();
            }
        }

        public override int Height
        {
            get { return _h; }
            set
            {
                if (value < 30) value = 30;
                _h = value;
                if (_parent != null) _parent.Render();
            }
        }

        #endregion

        #region Public Methods

        public void Refresh()
        {
            UpdateFiles();
            Render(true);
        }

        #endregion

        #region GUI

        public override void Render(bool flush)
        {
            if (_parent == null || _parent.ScreenBuffer == null || !_visible || _suspend) return;
            if (_items == null) UpdateFiles();

            // Set clipping region in case we're off parent somewhere (can happen w/ scroll)
            _parent.ScreenBuffer.SetClippingRectangle(_parent.Left, _parent.Top, _parent.Width, _parent.Height);

            // Draw the border & background
            _parent.ScreenBuffer.DrawRectangle(Colors.Black, 1, Left, Top, _w, _h, 0, 0, Colors.White, 0, 0, Colors.White, 0, 0, 256);

            int totalH = (_items.Count * 20) + 20;
            int y = _scrollY + Top + 21;
            bool bScroll = false;
            FileDetails fd;
            Font fnt = FontManager.Arial;
            int col2 = FontManager.ComputeExtentEx(fnt, "999.9 XX").Width;
            int col1 = _w - col2;

            rect Region = rect.intersect(new rect(_parent.Left, _parent.Top, _parent.Width, _parent.Height), new rect(Left + 1, Top + 1, _w - 2, _h - 2));
            _parent.ScreenBuffer.SetClippingRectangle(Region.X, Region.Y, Region.Width, Region.Height);

            if (totalH > _h)
            {
                bScroll = true;
                col1 -= 9;
            }

            // Draw the Items
            for (int i = 0; i < _items.Count; i++)
            {
                if (y > Top - 20)
                {
                    fd = (FileDetails)_items[i];

                    _parent.ScreenBuffer.DrawImage(Left + 3, y + 2, fd.Icon, 0, 0, fd.Icon.Width, fd.Icon.Height);
                    _parent.ScreenBuffer.DrawTextInRect(fd.Name, Left + fd.Icon.Width + 9, y + 3, col1 - 4, 20, Bitmap.DT_TrimmingCharacterEllipsis, (i == _selIndex) ? Colors.Blue : Colors.Black, fnt);
                    _parent.ScreenBuffer.DrawTextInRect(fd.Size, Left + col1 + 9, y + 3, col2 - 5, 20, Bitmap.DT_TrimmingCharacterEllipsis + Bitmap.DT_AlignmentLeft, (i == _selIndex) ? Colors.Blue : Colors.Black, fnt);
                }
                y += 20;
                if (y > Top + _h) break;
            }

            // Draw Columns
            _parent.ScreenBuffer.DrawRectangle(Colors.Black, 1, Left, Top, col1 + 1, 20, 0, 0, Colors.LightGray, 0, 0, Colors.LightGray, 0, 0, 256);
            _parent.ScreenBuffer.DrawTextInRect("Filename", Left + 4, Top + 3, col1 - 8, 16, Bitmap.DT_TrimmingCharacterEllipsis, Colors.Black, fnt);
            _parent.ScreenBuffer.DrawRectangle(Colors.Black, 1, Left + col1, Top, col2, 20, 0, 0, Colors.LightGray, 0, 0, Colors.LightGray, 0, 0, 256);
            _parent.ScreenBuffer.DrawTextInRect("Size", Left + +col1 + 4, Top + 3, col2 - 8, 16, Bitmap.DT_TrimmingCharacterEllipsis, Colors.Black, fnt);

            if (bScroll)
            {
                _parent.ScreenBuffer.DrawRectangle(Colors.DarkGray, 1, Left + _w - 9, Top + 1, 9, _h - 2, 0, 0, Colors.DarkGray, 0, 0, Colors.DarkGray, 0, 0, 256);

                float sH = (float)(totalH - _h);
                float sY = (sH == 0) ? 0 : -((float)_scrollY) / (float)(totalH - _h);
                int iSY = (int)((float)(_h - 24) * sY);
                _parent.ScreenBuffer.DrawRectangle(Colors.LightGray, 1, Left + _w - 9, Top + 1 + iSY, 9, 20, 0, 0, Colors.LightGray, 0, 0, Colors.LightGray, 0, 0, 256);
            }

            if (flush)
            {
                _parent.ScreenBuffer.Flush(Left, Top, _w, _h);
                _parent.ScreenBuffer.SetClippingRectangle(0, 0, AppearanceManager.ScreenWidth, AppearanceManager.ScreenHeight);
            }
        }

        #endregion

        #region Private Methods

        private void UpdateFiles()
        {
            string[] items;
            int i;
            FileDetails fd;

            if (_parent == null) return;
            Form frmParent = (Form)_parent.TopLevelContainer;

            // Reset Variables
            _selIndex = -1;
            _scrollY = 0;
            _mY = 0;
            OnSelectedFileChanged(this, "");

            // Reset Array List
            _items = new ArrayList();

            if (_path == string.Empty) return;
            if (!Directory.Exists(_path)) return;

            // Always get directories first
            items = Directory.GetDirectories(_path);
            for (i = 0; i < items.Length; i++)
            {
                fd = new FileDetails();
                fd.Name = items[i].Substring(items[i].LastIndexOf("\\") + 1);
                fd.Size = "";
                fd.Path = items[i];
                fd.Icon = Resources.GetBitmap(Resources.BitmapResources.folder);
                fd.IsFile = false;
                _items.Add(fd);
            }

            // Next Get Files
            string sExt;
            items = Directory.GetFiles(_path);
            for (i = 0; i < items.Length; i++)
            {
                sExt = System.IO.Path.GetExtension(items[i]).ToLower();
                if (FileExtentionInList(sExt))
                {
                    fd = new FileDetails();
                    fd.Name = System.IO.Path.GetFileName(items[i]);
                    fd.Path = items[i];
                    fd.Size = filelen(items[i]);
                    fd.Icon = frmParent.APIRef.MyFiles.GetAssociatedIcon(sExt);
                    fd.IsFile = true;
                    _items.Add(fd);
                }
            }

        }

        private bool FileExtentionInList(string ext)
        {
            if (_filter == null) return true;
            if (_filter[ext] != null) return true;
            return false;
        }

        private string filelen(string path)
        {
            try
            {
                FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
                long lng = fs.Length;
                fs.Close();

                if (lng < 1024) return lng + " B";
                lng = lng / 1024;

                if (lng < 1024) return lng + " KB";
                lng = lng / 1024;

                if (lng < 1024) return lng + " MB";
                lng = lng / 1024;

                return lng + " GB";
            }
            catch (Exception)
            {
                return "";
            }
        }

        #endregion

    }
}
